package ioc;

import di.BeanUtils;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Data
public class DefaultBeanFactory implements IBeanFactory,IBeanDefinitionRegistry, Closeable
{
    private final Log logger = LogFactory.getLog(this.getClass());

    private Map<String,IBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    private Map<String,Object> beanMap = new ConcurrentHashMap<>(256);

    public Object getBean(String beanName) throws Exception
    {
        return doGetBean(beanName);
    }

    private Object doGetBean(String beanName) throws Exception
    {
        Objects.requireNonNull(beanName,"beanName不能为Null");

        Object instance = beanMap.get(beanName);
        if(instance != null)
        {//如果不是null 代表是单例的Bean 已经执行过初始化等操作,可以直接返回
            return instance;
        }
        //如果没有获取到Bean对象,就得到Bean定义创建一个
        IBeanDefinition bd = beanDefinitionMap.get(beanName);
        //检查是否有class如果有,则直接反射创建,如果没有就从工厂中获取
        Class<?> cls = bd.getBeanClass();
        if(cls !=null)
        {
            if(StringUtils.isBlank(bd.getFactoryMethodName()))
            {//如果静厂方法不存在,则表明是直接构造方法
                instance = this.createInstanceByConstructor(bd);
            }else
            {
                instance = this.createInstanceByStaticMethod(bd);
            }
        }else
            {//如果没传类,代表需要从Bean工厂获取
                instance = this.createInstanceByFactoryMethod(bd);
            }
        //执行初始化操作
        this.doInit(bd,instance);

        if(bd.isSingleton())
        {//如果是单例Bean需要保存到map中去
            beanMap.put(beanName,instance);
        }
        return instance;
    }

    private void doInit(IBeanDefinition bd, Object instance) throws Exception {
        if(StringUtils.isNotBlank(bd.getInitMethodName()))
        {
            Method method = instance.getClass().getMethod(bd.getInitMethodName(),null);
            method.invoke(instance,null);
        }
    }

    private Object createInstanceByFactoryMethod(IBeanDefinition bd)throws Exception
    {
        Object factoryBean = this.doGetBean(bd.getFactoryBeanName());
        Object[] params = BeanUtils.getRealValues(bd.getPropertyValues());
        Method method = BeanUtils.determineMethod(bd,params,factoryBean.getClass());
        return method.invoke(factoryBean,params);
    }

    private Object createInstanceByStaticMethod(IBeanDefinition bd) throws Exception
    {
        Class cls = bd.getBeanClass();
        Object[] params = BeanUtils.getRealValues(bd.getPropertyValues());
        Method method = BeanUtils.determineMethod(bd,params,null);
        return method.invoke(cls,params);
    }

    private Object createInstanceByConstructor(IBeanDefinition bd) throws Exception
    {
      Object[] objects = BeanUtils.getRealValues( bd.getConstructorArgumentValues());
      if(objects==null)
      {
          return bd.getBeanClass().newInstance();
      }
      return BeanUtils.determineConstructor(bd,objects).newInstance(objects);
    }

    /**
     * 注册Bean
     */
    public void registerBeanDefinition(String beanName,IBeanDefinition bd) throws BeanDefinitionRegisterException {
        Objects.requireNonNull(beanName,"需要注册的beanName不能为NULL");
        Objects.requireNonNull(bd,"需要注册的beanDefinition不能为NULL");
        if(!bd.validate())
        {
            throw new BeanDefinitionRegisterException("名字为[" + beanName + "] 的bean定义不合法：" + bd);
        }
        if(this.containsBeanDefinition(beanName))
        {
            throw new BeanDefinitionRegisterException("名字为[" + beanName + "] 的bean定义已存在,不能重复定义：" + bd);
        }
        this.beanDefinitionMap.put(beanName,bd);
    }

    public IBeanDefinition getBeanDefinition(String beanName)
    {
        return beanDefinitionMap.get(beanName);
    }

    public boolean containsBeanDefinition(String beanName)
    {
        return beanDefinitionMap.containsKey(beanName);
    }

    /**
     * 监听关闭事件,好执行关闭方法
     */
    public void close() throws IOException
    {//因为此时只有单例的Bean归IOC容器管理,所以这里只能实现单例的Bean关闭的方法
        Set<Map.Entry<String,IBeanDefinition>> entry = beanDefinitionMap.entrySet();
        entry.forEach( e ->
        {
            IBeanDefinition bd = e.getValue();
            if(bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName()))
            {//如果销毁方法不是null
                Object obj = beanMap.get(e.getKey());
                try {
                    Method method = obj.getClass().getMethod(bd.getDestroyMethodName(),null);
                    method.invoke(obj,null);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        });
    }

}
